using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Castle.ActiveRecord.Queries;
using Castle.ActiveRecord;
using NHibernate;
using System.Collections;
using System.IO;
using System.Xml;

namespace Stormwind.Core.Data {
	/// <summary>
	/// Base query for executing hql.
	/// </summary>
	public class BaseQuery : ActiveRecordBaseQuery {
		/// <summary>
		/// Default constructor.
		/// </summary>
		/// <param name="baseType">Base type.</param>
		public BaseQuery(Type baseType)
			: base(baseType) {
			this.baseType = baseType;
		}
		private string hql;
		/// <summary>
		/// HQL to be executed.
		/// </summary>
		public string Hql {
			get {
				return hql;
			}
			set {
				hql = value;
			}
		}

		private Type baseType;
		/// <summary>
		/// Base type.
		/// </summary>
		public Type BaseType {
			get {
				return baseType;
			}
			set {
				baseType = value;
			}
		}

		/// <summary>
		/// Creates the query being executed.
		/// </summary>
		/// <param name="session">Session.</param>
		/// <returns>Returns the query.</returns>
		protected override IQuery CreateQuery(ISession session) {
			IQuery q = session.CreateQuery(hql);
			return q;
		}
	}

	/// <summary>
	/// Active Record Data Broker.
	/// </summary>
	public class ARDataBroker : IDataBroker {

		#region IDataBroker Members

		/// <summary>
		/// Get data from an Active Record model.
		/// </summary>
		/// <param name="def">Data Definition.</param>
		/// <returns>Returns the xml with the retrieved data.</returns>
		public DataTable GetData(DataDefinition def) {
			StringBuilder sb = new StringBuilder();
			sb.Append("SELECT ");
			sb.Append(GetFields(def));
			sb.Append(" FROM ");
			sb.Append(GetFrom(def));
			if (def.Relations.Count > 0) {
				sb.Append(" WHERE ");
				sb.Append(GetWhere(def));
			}
			sb.Append(" ORDER BY ");
			sb.Append(GetOrder(def));

			string hql = sb.ToString();
			Type t = Type.GetType(string.Format("{0}.{1}, {2}", def.DataSources[0].ClassNamespace, def.DataSources[0].ClassName, def.DataSources[0].AssemblyName));
			//SimpleQuery q = new SimpleQuery(t, hql);
			BaseQuery q = new BaseQuery(t);
			q.Hql = hql;
			DataTable dt = ConvertToTable(def, (ArrayList)ActiveRecordBase.ExecuteQuery(q));
			
			return dt;
		}

		#region Generate HQL
		/// <summary>
		/// Gets the fields being selected.
		/// </summary>
		/// <param name="def">Data Definition.</param>
		/// <returns>Returns the fields string separated by comma.</returns>
		protected string GetFields(DataDefinition def) {
			StringBuilder sb = new StringBuilder();
			int i = 1;
			foreach (FieldDefinition fd in def.Fields) {
				sb.AppendFormat("{0}.{1}", fd.DataSource.ClassName, fd.PropertyName);
				if (i < def.Fields.Count) {
					sb.Append(", ");
				}
				i++;
			}

			return sb.ToString();
		}

		/// <summary>
		/// Get the objects to load data from.
		/// </summary>
		/// <param name="def">Data Definition.</param>
		/// <returns>Returns the objects string separated by comma.</returns>
		protected string GetFrom(DataDefinition def) {
			StringBuilder sb = new StringBuilder();
			int i = 1;
			foreach (DataSource ds in def.DataSources) {
				sb.AppendFormat("{0} as {1}", ds.ClassName, ds.ClassName);
				if (i < def.DataSources.Count) {
					sb.Append(", ");
				}
				i++;
			}

			return sb.ToString();
		}

		/// <summary>
		/// Gets the relations between data sources as well as filter conditions.
		/// </summary>
		/// <param name="def">Data Definition.</param>
		/// <returns>Returns the filters string separated by comma.</returns>
		protected string GetWhere(DataDefinition def) {
			StringBuilder sb = new StringBuilder();
			sb.Append(GetRelations(def));
			//if (def.Filters.Count > 0 && def.Relations.Count > 0) {
			//  sb.Append(" AND ")
			//}
			//sb.Append(GetFilters(def));

			return sb.ToString();
		}

		/// <summary>
		/// Gets the relations string.
		/// </summary>
		/// <param name="def">Data Definition.</param>
		/// <returns>Returns the relations string separated by comma.</returns>
		protected string GetRelations(DataDefinition def) {
			StringBuilder sb = new StringBuilder();
			int i = 1;
			foreach (DataRelation dr in def.Relations) {
				sb.Append(dr.Origin.ClassName);
				if (!string.IsNullOrEmpty(dr.OriginProperty)) {
					sb.Append(".");
					sb.Append(dr.OriginProperty);
				}
				sb.Append("=");
				sb.Append(dr.Destiny.ClassName);
				if (!string.IsNullOrEmpty(dr.DestinyProperty)) {
					sb.Append(".");
					sb.Append(dr.DestinyProperty);
				}
				if (i < def.Relations.Count) {
					sb.Append("and");
				}
				i++;
			}

			return sb.ToString();
		}

		/// <summary>
		/// Gets the order by statement.
		/// </summary>
		/// <param name="def">Data Definition</param>
		/// <returns>Returns the order by fields.</returns>
		protected string GetOrder(DataDefinition def) {
			StringBuilder sb = new StringBuilder();
			int i = 1;
			foreach (GroupingDefinition gd in def.Groupings) {
				sb.Append(string.Format("{0}.{1}", gd.Field.DataSource.ClassName, gd.Field.PropertyName));
				if (i < def.Groupings.Count) {
					sb.Append(",");
				}
				i++;
			}

			return sb.ToString();
		}
		#endregion Generate HQL

		#region Convert to Table
		/// <summary>
		/// Returns the datatable converted from the HQL result.
		/// </summary>
		/// <param name="def">Data Definition.</param>
		/// <param name="result">Result from the HQL Query.</param>
		/// <returns>Returns the data table.</returns>
		protected DataTable ConvertToTable(DataDefinition def, ArrayList result) {
			DataTable dt = new DataTable("DynamicTable");
			foreach (FieldDefinition df in def.Fields) {
				dt.Columns.Add(string.Format("{0}.{1}", df.DataSource.ClassName, df.PropertyName));
			}
			foreach (object[] row in result) {
				DataRow dr = dt.NewRow();
				int i = 0;
				foreach (object val in row) {
					dr[i] = val;
					i++;
				}
				dt.Rows.Add(dr);
			}

			return dt;
		}
		#endregion Convert to Table

		#endregion
	}
}
